<?php
require_once 'PEAR/Exception.php';
require_once 'MDB2.php';

class VersionControl_SVN_Authz_Database
{
    /**
     * @var object
     */
    protected $db;

    /**
     * @var constant
     */
    const DB_DSN = 'mysqli://%s:%s@localhost/pear_svn';

    /**
     * setDb
     *
     * @access public
     */
    public function setDb($db = null, $db_user = null, $db_pass = null)
    {
        if (is_null($db) === false) {
            $this->db = $db;
        }
        $dsn = sprintf(self::DB_DSN, $db_user, $db_pass);

        $this->db = MDB2::connect($dsn);
        if (MDB2::isError($this->db)) {
            self::castToException($this->db);
        }
        return true;
    }

    public function addUser($user, $password, $active = true)
    {
        if (!is_string($user) || empty($user)) {
            return false;
        }

        $exists = $this->getUser($user);
        if (!empty($exists)) {
            return false;
        }

        // MD5 password
        if (!$active && (!is_string($password) || strlen($password) !== 32)) {
            return false;
        }

        $sql = 'INSERT INTO users
        (user_name, user_passwd, active)
        VALUES (' .
            $this->db->quote($user, 'text')
        . ', ' .
            $this->db->quote($password, 'text')
        . ', ' .
            $this->db->quote($active, 'boolean')
        . ')';
        $result = $this->db->exec($sql);
        if (MDB2::isError($result)) {
            self::castToException($result);
        }

        return true;
    }

    public function removeUser($user)
    {
        if (!is_string($user) || empty($user)) {
            return false;
        }

        $sql = 'DELETE FROM users WHERE user_name = ';
        $sql.= $this->db->quote($user, 'text');
        $result = $this->db->exec($sql);
        if (MDB2::isError($result)) {
            self::castToException($result);
        }

        return true;
    }

    public function addGroup($name)
    {
        if (!is_string($name) || empty($name)) {
            return false;
        }

        $exists = $this->groupExists($name);
        if ($exists) {
            return false;
        }

        $sql = 'INSERT INTO groups (group_name)
        VALUES(' . $this->db->quote($name, 'text') . ')';
        $result = $this->db->exec($sql);
        if (MDB2::isError($result)) {
            self::castToException($result);
        }

        return true;
    }

    public function removeGroup($name)
    {
        if (!is_string($name) || empty($name)) {
            return false;
        }

        $sql = 'DELETE FROM groups WHERE group_name = ';
        $sql.= $this->db->quote($name, 'text');
        $result = $this->db->exec($sql);
        if (MDB2::isError($result)) {
            self::castToException($result);
        }

        return true;
    }

    public function addUserToGroup($user, $group)
    {
        if (!is_string($user) || empty($user) || !is_string($group) || empty($group)) {
            return false;
        }

        $res = $this->groupExists($group);
        if (!$res) {
            return false;
        }

        $res = $this->getUser($user);
        if (empty($res)) {
            return false;
        }

        $sql = 'INSERT INTO groups_users (user_name, group_name)
        VALUES(' .
             $this->db->quote($user, 'text')
        . ', ' .
            $this->db->quote($group, 'text')
        . ')';
        $result = $this->db->exec($sql);
        if (MDB2::isError($result)) {
            self::castToException($result);
        }

        return true;
    }

    public function removeUserFromGroup($user, $group)
    {
        if (empty($user) || empty($group)) {
            return false;
        }

        $sql = 'DELETE FROM groups_users WHERE user_name = ' .
            $this->db->quote($user, 'text')
        . ' AND group_name = ' .
            $this->db->quote($group, 'text');
        $result = $this->db->exec($sql);
        if (MDB2::isError($result)) {
            self::castToException($result);
        }

        return true;
    }

    public function addPermission($group, $path, $perm)
    {
        $exists = $this->existsPermission($perm);


        $sql = 'INSERT INTO permission (group_name, path, permission) VALUES(' .
            $this->db->quote($group, 'text')
        . ', ' .
            $this->db->quote($path, 'text')
        . ', ' .
            $this->db->quote($perm, 'text')
        . ')';
        $result = $this->db->exec($sql);
        if (MDB2::isError($result)) {
            self::castToException($result);
        }

        return true;
    }

    public function removePermission($group, $path, $perm)
    {
        $sql = 'DELETE FROM permission WHERE';
        $sql.= ' group_name = '     . $this->db->quote($group, 'text');
        $sql.= ' AND path = '       . $this->db->quote($path, 'text');
        $sql.= ' AND permission = ' . $this->db->quote($perm, 'text');
        $result = $this->db->exec($sql);
        if (MDB2::isError($result)) {
            self::castToException($result);
        }

        return true;
    }

    public function groupExists($group)
    {
        $sql = 'SELECT group_name FROM groups';
        $res = $this->db->queryOne($sql);
        if (MDB2::isError($res)) {
            self::castToException($res);
        }

        if (empty($res)) {
            return false;
        }

        return true;
    }

    public function getGroups($getUsers = true)
    {
        $sql = 'SELECT DISTINCT group_name FROM groups';
        $this->db->setFetchMode(MDB2_FETCHMODE_ASSOC);

        $groups = $this->db->queryAll($sql);
        if (MDB2::isError($groups)) {
            self::castToException($groups);
        }
        if (empty($groups)) {
            return $groups;
        }

        if ($getUsers === true) {
            $groups_count = count($groups);
            for ($x = 0; $x < $groups_count; $x++) {
                $groups[$x]['users'] = $this->getUsersFromGroup($groups[$x]['group_name']);
            }
        }

        return $groups;
    }

    public function getUsersFromGroup($group_name)
    {
        if (empty($group_name)) {
            return false;
        }

        $sql = 'SELECT user_name FROM groups_users';
        $sql.= ' WHERE group_name = ' . $this->db->quote($group_name);
        $users = $this->db->queryCol($sql);
        if (MDB2::isError($users)) {
            castToException($users);
        }
        return $users;
    }

    public function getUser($handle)
    {
        if (empty($handle)) {
            return false;
        }

        $sql = 'SELECT user_name FROM users WHERE user_name = ';
        $sql.= $this->db->quote($handle, 'text');

        $user = $this->db->queryOne($sql);
        if (MDB2::isError($user)) {
            castToException($user);
        }
        return $user;
    }

    public function getPermissions()
    {
        $sql = 'SELECT path, group_name, permission FROM permission';
        $sql.= ' ORDER BY path ASC';
        $perms = $this->db->queryAll($sql);
        if (MDB2::isError($perms)) {
            castToException($perms);
        }
        return $perms;
    }

    function existsPermission($path)
    {
        $sql = 'SELECT path FROM permission WHERE path = ';
        $sql.= $this->db->quote($perm, 'text');
        $res = $this->db->queryOne($sql);
        if (MDB2::isError($res)) {
            castToException($res);
        }

        if (empty($res)) {
            return false;
        }

        return true;
    }

    static function castToException($errObj)
    {
        throw new VersionControl_SVN_Authz_Database_Exception(
                    $errObj->getDebugInfo(),
                    $errObj->getCode()
        );
    }
}

/**
 * VersionControl_SVN_Authz_Database_Exception - duh
 */
class VersionControl_SVN_Authz_Database_Exception extends PEAR_Exception {}